`include "defines.v"
module ifu(
  input clk,
  input rst_n,
  input [`VADDR_W-1: 0] boot_addr,
  // commit 的 异常处理信号
  input                 commit_ifu_exception_i,
  input [`VADDR_W-1:0]  commit_ifu_exceptionPC_i,
  // bru 的 错误分支预测修正信号
  input                 bru_ifu_valid_i,
  input [`VADDR_W-1:0]  bru_ifu_JumpOldPC_i,
`ifdef PREDICT_PC
  // RAS 
  input                 ras_ifu_valid_i,
  input [`VADDR_W-1:0]  ras_ifu_addr_i,
  // BTB & BHT
  input                 bpu_ifu_valid_i,
  input [`VADDR_W-1:0]  bpu_ifu_predict_addr_i,
  output [`VADDR_W-1:0] ifu_bpu_pc_o,
`endif
  // icache
  output                ifu_icache_valid_o,
  input                 icache_ifu_ready_i,
  output [`VADDR_W-1:0] ifu_icache_req_addr_o,
  output                ifu_icache_pre_o
`ifdef PREDICT_PC
  // baq
  ,input                 baq_ifu_ready_i,
  output                ifu_baq_valid_o,      
  output [`VADDR_W-1:0] ifu_baq_din_o     
`endif
);  
// 更新的关键在于 
// 空间上commit 越近 跳转猜测正确的概率越大
// 时间上 越老的信号正确性越高
// 预测地址 错误也不会造成正确性问题 因为会在BRU阶段修正
reg  [`VADDR_W-1:0] pc ;
reg  [`VADDR_W  :0] jump_entry; // V + addr
wire [`VADDR_W-1:0] npc;
wire pcUpdate;
assign pcUpdate = ifu_icache_valid_o && icache_ifu_ready_i;
// address select
// wire [`VADDR_W-1:0] boot_addr;
// assign boot_addr = (boot == `BOOT_ROM) ? `BOOT_ROM_START :
//                    (boot == `BOOT_SD ) ? `BOOT_SD_START  :
//                    (boot == `BOOT_MEM) ? `BOOT_MEM_START :`BOOT_SD_START;
// process counter update
always@(posedge clk or  negedge rst_n)
  if(~rst_n)
    pc <= boot_addr - 'd4;
  else if(pcUpdate)
    pc <= npc;
// 当程序流改变 且不处于刷新pc阶段时 将信息保存到jump-entry里等待
// 异常刷新优先级大于分支刷新
// 下一级握手后 jump-entry失效
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    jump_entry[`VADDR_W] <= 1'b0;
  else if(pcUpdate)
    jump_entry[`ADDR_W] <= 1'b0;
  else if(commit_ifu_exception_i && ~pcUpdate)
    jump_entry <= {1'b1,commit_ifu_exceptionPC_i};
  else if(bru_ifu_valid_i && ~pcUpdate)
    jump_entry <= {1'b1,bru_ifu_JumpOldPC_i};
`ifdef PREDICT_PC
  else if(ras_ifu_valid_i && ~pcUpdate)
    jump_entry <= {1'b1,ras_ifu_addr_i};
`endif
// jump-entry 是最早的刷新信息 所以优先级最高
// 异常刷新 大于 分支刷新
// 然后是 预测的指令 ras > btb
// 最后才是 PC的下一条指令地址
assign npc = jump_entry[`VADDR_W]   ? jump_entry[`VADDR_W-1:0] : 
             commit_ifu_exception_i ? commit_ifu_exceptionPC_i :
             bru_ifu_valid_i        ? bru_ifu_JumpOldPC_i      :
`ifdef PREDICT_PC
             ras_ifu_valid_i        ? ras_ifu_addr_i           :
             bpu_ifu_valid_i        ? bpu_ifu_predict_addr_i   :
`endif
             pc + 'd4;
// Icache
// 1有预测时要看 BAQ有空   2没有预测 直接发射
`ifdef PREDICT_PC
assign ifu_icache_valid_o     = (bpu_ifu_valid_i && baq_ifu_ready_i) || ~bpu_ifu_valid_i;
assign ifu_icache_req_addr_o  = npc;
assign ifu_icache_pre_o       = bpu_ifu_valid_i;
`else 
assign ifu_icache_valid_o     = 1'b1;
assign ifu_icache_req_addr_o  = npc;
assign ifu_icache_pre_o       = 1'b0;
`endif

// BAQ
`ifdef PREDICT_PC
  assign ifu_baq_valid_o  = ifu_icache_valid_o && icache_ifu_ready_i && bpu_ifu_valid_i;
  assign ifu_baq_din_o    = bpu_ifu_predict_addr_i     ;
  assign ifu_bpu_pc_o  = pc;
`endif

endmodule